package org.dromara.generator.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.RegExUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.generator.config.GenConfig;
import org.dromara.generator.constant.GenConstants;
import org.dromara.generator.domain.GenTable;
import org.dromara.generator.domain.GenTableColumn;

import java.util.Arrays;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：代码生成器 工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class GenUtils {

    /**
     * 初始化表信息
     * 初始化代码生成的GenTable对象
     * 该方法主要用于设置代码生成时必要的信息，如类名、包名、模块名、业务名、功能名、作者和创建者ID
     *
     * @param genTable 代码生成的配置对象，包含生成的各项信息
     * @param operId 操作者ID，用于记录代码生成的创建者
     */
    public static void initTable(GenTable genTable, Long operId) {
        // 将数据库表名转换为Java类名
        genTable.setClassName(convertClassName(genTable.getTableName()));
        // 设置生成代码的包名
        genTable.setPackageName(GenConfig.getPackageName());
        // 从包名中提取模块名
        genTable.setModuleName(getModuleName(GenConfig.getPackageName()));
        // 从表名中提取业务名
        genTable.setBusinessName(getBusinessName(genTable.getTableName()));
        // 替换功能名中的特定字符
        genTable.setFunctionName(replaceText(genTable.getTableComment()));
        // 设置生成代码的作者
        genTable.setFunctionAuthor(GenConfig.getAuthor());
        // 设置创建者ID
        genTable.setCreateBy(operId);
    }

    /**
     * 初始化列属性字段
     */
    public static void initColumnField(GenTableColumn column, GenTable table) {
        // 根据列的类型获取数据库类型字符串表示
        String dataType = getDbType(column.getColumnType());
        // 获取列的名称
        String columnName = column.getColumnName();
        // 设置列所属的表ID
        column.setTableId(table.getTableId());
        // 设置列的创建者
        column.setCreateBy(table.getCreateBy());
        // 设置java字段名
        column.setJavaField(StringUtils.toCamelCase(columnName));
        // 设置默认类型 字符串类型
        column.setJavaType(GenConstants.TYPE_STRING);
        // 相等查询
        column.setQueryType(GenConstants.QUERY_EQ);

        // 如果数据类型数组中包含数据库字符串类型或数据库文本类型
        if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) {
            // 字符串长度超过500设置为文本域
            Integer columnLength = getColumnLength(column.getColumnType());
            // 根据列长度和数据类型确定HTML元素类型
            // 当列长度大于等于500，或者数据类型属于文本类型数组时，选择<textarea>元素，否则选择<input>元素
            String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
            // 设置列的HTML类型
            column.setHtmlType(htmlType);
            // 若GenConstants.COLUMNTYPE_TIME数组中包含指定的数据库时间类型
        } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) {
            // 如果数据类型为时间类型，设置Java类型为日期，并设置HTML类型为日期时间选择器
            column.setJavaType(GenConstants.TYPE_DATE);
            // 日期控件
            column.setHtmlType(GenConstants.HTML_DATETIME);
            // 判断数据类型是否为数据库数字类型
        } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) {
            // 设置HTML类型为文本框
            column.setHtmlType(GenConstants.HTML_INPUT);

            // 如果是浮点型 统一用BigDecimal
            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), StringUtils.SEPARATOR);
            // 检查字符串数组是否为非空，并且其第二个元素作为整数大于0
            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {
                // 高精度计算类型
                column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
            }
            // 如果是整形 当字符串非空且仅包含一个元素时，检查该元素是否为数字且值小于等于10
            else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) {
                // 整型
                column.setJavaType(GenConstants.TYPE_INTEGER);
            }
            // 长整形
            else {
                // 长整型
                column.setJavaType(GenConstants.TYPE_LONG);
            }
        }

        // BO对象 默认插入勾选 BO对象
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) {
            column.setIsInsert(GenConstants.REQUIRE);
        }
        // BO对象 默认编辑勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) {
            column.setIsEdit(GenConstants.REQUIRE);
        }
        // BO对象 默认是否必填勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) {
            column.setIsRequired(GenConstants.REQUIRE);
        }
        // VO对象 默认返回勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) {
            column.setIsList(GenConstants.REQUIRE);
        }
        // BO对象 默认查询勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) {
            column.setIsQuery(GenConstants.REQUIRE);
        }

        // 查询字段类型
        if (StringUtils.endsWithIgnoreCase(columnName, "name")) {
            column.setQueryType(GenConstants.QUERY_LIKE);
        }
        // 状态字段设置单选框
        if (StringUtils.endsWithIgnoreCase(columnName, "status")) {
            column.setHtmlType(GenConstants.HTML_RADIO);
        }
        // 类型&性别字段设置下拉框
        else if (StringUtils.endsWithIgnoreCase(columnName, "type")
            || StringUtils.endsWithIgnoreCase(columnName, "sex")) {
            column.setHtmlType(GenConstants.HTML_SELECT);
        }
        // 图片字段设置图片上传控件
        else if (StringUtils.endsWithIgnoreCase(columnName, "image")) {
            column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);
        }
        // 文件字段设置文件上传控件
        else if (StringUtils.endsWithIgnoreCase(columnName, "file")) {
            column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);
        }
        // 内容字段设置富文本控件
        else if (StringUtils.endsWithIgnoreCase(columnName, "content")) {
            column.setHtmlType(GenConstants.HTML_EDITOR);
        }
    }

    /**
     * 校验数组是否包含指定值
     *
     * @param arr         数组
     * @param targetValue 值
     * @return 是否包含
     */
    public static boolean arraysContains(String[] arr, String targetValue) {
        return Arrays.asList(arr).contains(targetValue);
    }

    /**
     * 获取模块名
     *
     * @param packageName 包名
     * @return 模块名
     */
    public static String getModuleName(String packageName) {
        // 获取包名的最后一部分，即类名
        int lastIndex = packageName.lastIndexOf(".");
        // 获取packageName的长度
        int nameLength = packageName.length();
        // 从packageName中提取子字符串，从lastIndex之后的位置开始，到nameLength指定的长度
        // 这里的目的是获取packageName中从指定位置之后的子串
        return StringUtils.substring(packageName, lastIndex + 1, nameLength);
    }

    /**
     * 获取业务名
     *
     * @param tableName 表名
     * @return 业务名
     */
    public static String getBusinessName(String tableName) {
        // 查找表名中第一个下划线的位置
        int firstIndex = tableName.indexOf("_");
        // 获取表名的长度
        int nameLength = tableName.length();
        // 从下划线后面的位置到表名末尾，提取字符串片段作为业务名
        String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength);
        // 将业务名转换为驼峰命名法
        businessName = StringUtils.toCamelCase(businessName);
        // 返回转换后的业务名
        return businessName;
    }

    /**
     * 表名转换成Java类名
     *
     * @param tableName 表名称
     * @return 类名
     */
    public static String convertClassName(String tableName) {
        // 获取是否开启自动去除表前缀的配置
        boolean autoRemovePre = GenConfig.getAutoRemovePre();
        // 获取表前缀配置
        String tablePrefix = GenConfig.getTablePrefix();
        // 如果开启自动去除表前缀且表前缀配置不为空，则处理tableName
        if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {
            // 将表前缀配置按逗号分隔成数组
            String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR);
            // 替换tableName中第一个匹配的表前缀
            tableName = replaceFirst(tableName, searchList);
        }
        // 将处理后的tableName转换为驼峰命名法并返回
        return StringUtils.convertToCamelCase(tableName);
    }

    /**
     * 批量替换前缀
     *
     * @param replacementm 替换值
     * @param searchList   替换列表
     */
    public static String replaceFirst(String replacementm, String[] searchList) {
        // 根据 searchList 中的搜索字符串列表，替换 replacementm 字符串中特定前缀
        String text = replacementm;
        for (String searchString : searchList) {
            // 检查 replacementm 是否以 searchString 开头
            if (replacementm.startsWith(searchString)) {
                // 替换掉 replacementm 中的第一个 searchString 实例为空字符串
                text = replacementm.replaceFirst(searchString, StringUtils.EMPTY);
                // 找到并替换后，跳出循环
                break;
            }
        }
        // 返回处理后的文本
        return text;
    }

    /**
     * 关键字替换
     *
     * @param text 需要被替换的名字
     * @return 替换后的名字
     */
    public static String replaceText(String text) {
        // 使用RegExUtils类的replaceAll方法替换并移除文本中出现的"表"或"若依"字样
        // 这里的正则表达式"(?:表|若依)"表示非贪婪模式下匹配"表"或"若依"，确保了替换功能的灵活性和高效性
        return RegExUtils.replaceAll(text, "(?:表|若依)", "");
    }

    /**
     * 获取数据库类型字段
     *
     * @param columnType 列类型
     * @return 截取后的列类型
     */
    public static String getDbType(String columnType) {
        // 当列类型包含括号时，返回括号前的子字符串；否则返回原始列类型
        if (StringUtils.indexOf(columnType, "(") > 0) {
            return StringUtils.substringBefore(columnType, "(");
        } else {
            return columnType;
        }
    }

    /**
     * 获取字段长度
     *
     * @param columnType 列类型
     * @return 截取后的列类型
     */
    public static Integer getColumnLength(String columnType) {
        if (StringUtils.indexOf(columnType, "(") > 0) {
            // 使用 StringUtils 提取列类型中的长度部分，该长度被括号包围
            String length = StringUtils.substringBetween(columnType, "(", ")");
            // 将提取的长度信息转换为 Integer 类型并返回
            return Integer.valueOf(length);
        } else {
            // 如果列类型中不包含长度信息，则返回默认值 0
            return 0;
        }
    }
}
